// ========== Copyright Header Begin ==========================================
// 
// OpenSPARC T1 Processor File: ucb_noflow.v
// Copyright (c) 2006 Sun Microsystems, Inc.  All Rights Reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
// 
// The above named program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License version 2 as published by the Free Software Foundation.
// 
// The above named program is distributed in the hope that it will be 
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with this work; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
// 
// ========== Copyright Header End ============================================
////////////////////////////////////////////////////////////////////////
/*
//  Module Name:        ucb_noflow
//	Description:	Unit Control Block
//                      - supports 64 or 128-bit read with flow control
//                      - supports 64-bit write without flow control
//                      - automactically drops non-64-bit writes
//                      - supports interrupt return to IO Bridge
//                      - provides only single buffer at each interface 
//
//                      This module is intended for units that have
//                      both 64 and 128 bit registers.
//
//                      Data bus width to and from the IO Bridge is
//                      configured through parameters UCB_IOB_WIDTH and
//                      IOB_UCB_WIDTH.  Supported widths are:
//
//                      IOB_UCB_WIDTH  UCB_IOB_WIDTH
//                      ----------------------------
//                      32             8
//                      16             8
//                       8             8
//                       4             4             
 */ 
////////////////////////////////////////////////////////////////////////
// Global header file includes
////////////////////////////////////////////////////////////////////////
`include	"sys.h" // system level definition file which 
                        // contains the time scale definition

`include        "iop.h"

////////////////////////////////////////////////////////////////////////
// Local header file includes / local defines
////////////////////////////////////////////////////////////////////////

module ucb_noflow (/*AUTOARG*/
   // Outputs
   ucb_iob_stall, rd_req_vld, wr_req_vld, thr_id_in, buf_id_in, 
   size_in, addr_in, data_in, int_busy, ucb_iob_vld, ucb_iob_data, 
   // Inputs
   clk, rst_l, iob_ucb_vld, iob_ucb_data, rd_ack_vld, rd_nack_vld, 
   thr_id_out, buf_id_out, data128, data_out, int_vld, int_typ, 
   int_thr_id, dev_id, int_stat, int_vec, iob_ucb_stall
   );

   // synopsys template
   
   parameter IOB_UCB_WIDTH = 32;  // data bus width from IOB to UCB
   parameter UCB_IOB_WIDTH = 8;   // data bus width from UCB to IOB
   parameter REG_WIDTH = 64;      // set this to 128 if unit needs to
                                  // return 128-bit data
   

   // Globals
   input                                clk;
   input 				rst_l;
   
   // Request from IO Bridge
   input 				iob_ucb_vld;
   input [IOB_UCB_WIDTH-1:0] 		iob_ucb_data;
   output 				ucb_iob_stall;

   // Request to local unit
   output 				rd_req_vld;
   output 				wr_req_vld;
   output [`UCB_THR_HI-`UCB_THR_LO:0]   thr_id_in;
   output [`UCB_BUF_HI-`UCB_BUF_LO:0]   buf_id_in;
   output [`UCB_SIZE_HI-`UCB_SIZE_LO:0] size_in;   // only pertinent to PCI
   output [`UCB_ADDR_HI-`UCB_ADDR_LO:0] addr_in;
   output [`UCB_DATA_HI-`UCB_DATA_LO:0] data_in;

   // Ack/Nack from local unit
   input 				rd_ack_vld;
   input 				rd_nack_vld;
   input [`UCB_THR_HI-`UCB_THR_LO:0] 	thr_id_out;
   input [`UCB_BUF_HI-`UCB_BUF_LO:0] 	buf_id_out;
   input 				data128;   // set to 1 if data returned is 128 bit
   input [REG_WIDTH-1:0] 		data_out;

   // Interrupt from local unit
   input 				int_vld;
   input [`UCB_PKT_HI-`UCB_PKT_LO:0] 	int_typ;          // interrupt type
   input [`UCB_THR_HI-`UCB_THR_LO:0] 	int_thr_id;       // interrupt thread ID
   input [`UCB_INT_DEV_HI-`UCB_INT_DEV_LO:0] dev_id;      // interrupt device ID
   input [`UCB_INT_STAT_HI-`UCB_INT_STAT_LO:0] int_stat;  // interrupt status
   input [`UCB_INT_VEC_HI-`UCB_INT_VEC_LO:0]   int_vec;   // interrupt vector
   output 				int_busy;         // interrupt buffer busy
   
   // Output to IO Bridge
   output 				ucb_iob_vld;
   output [UCB_IOB_WIDTH-1:0] 		ucb_iob_data;
   input 				iob_ucb_stall;
   
   // Local signals
   wire 				indata_buf_vld;
   wire [127:0] 			indata_buf;
   wire 				ucb_iob_stall_a1;
   
   wire 				read_pending;
   wire 				read_outstanding;
   wire 				read_outstanding_next;
   wire 				write_pending;
   wire 				illegal_write_size;
   
   wire 				ack_buf_rd;
   wire 				ack_buf_wr;
   wire 				ack_buf_vld;
   wire 				ack_buf_vld_next;
   wire 				ack_buf_is_nack;
   wire 				ack_buf_is_data128;
   wire [`UCB_PKT_HI-`UCB_PKT_LO:0] 	ack_typ_out;
   wire [REG_WIDTH+`UCB_BUF_HI-`UCB_PKT_LO:0] ack_buf_in;
   wire [REG_WIDTH+`UCB_BUF_HI-`UCB_PKT_LO:0] ack_buf;
   wire [(REG_WIDTH+64)/UCB_IOB_WIDTH-1:0] ack_buf_vec;
   
   wire 				int_buf_rd;
   wire 				int_buf_wr;
   wire 				int_buf_vld;
   wire 				int_buf_vld_next;
   wire [`UCB_INT_VEC_HI-`UCB_PKT_LO:0] int_buf_in;
   wire [`UCB_INT_VEC_HI-`UCB_PKT_LO:0] int_buf;
   wire [(REG_WIDTH+64)/UCB_IOB_WIDTH-1:0] int_buf_vec;
   
   wire 				int_last_rd;
   wire 				outdata_buf_busy;
   wire 				outdata_buf_wr;
   wire [REG_WIDTH+63:0] 		outdata_buf_in;
   wire [(REG_WIDTH+64)/UCB_IOB_WIDTH-1:0] outdata_vec_in;


////////////////////////////////////////////////////////////////////////
// Code starts here
////////////////////////////////////////////////////////////////////////
   /************************************************************
    * Inbound Data
    ************************************************************/
   // Register size is hardcoded to 64 bits here because all
   // units using the UCB module will only write to 64 bit registers.
   ucb_bus_in #(IOB_UCB_WIDTH,64) ucb_bus_in (.rst_l(rst_l),
					      .clk(clk),
					      .vld(iob_ucb_vld),
					      .data(iob_ucb_data),
					      .stall(ucb_iob_stall),
					      .indata_buf_vld(indata_buf_vld),
					      .indata_buf(indata_buf),
					      .stall_a1(ucb_iob_stall_a1));

   
   /************************************************************
    * Decode inbound packet type
    ************************************************************/
   assign 	 read_pending = (indata_buf[`UCB_PKT_HI:`UCB_PKT_LO] ==
				 `UCB_READ_REQ) &
			        indata_buf_vld;

   // Assertion: rd_req_vld and ack_buf_rd must be
   //            mutually exclusive
   assign 	 read_outstanding_next = rd_req_vld ? 1'b1 :
	                                 ack_buf_rd ? 1'b0 :
	                                              read_outstanding;
   dffrl_ns #(1) read_outstanding_ff (.din(read_outstanding_next),
				      .clk(clk),
				      .rst_l(rst_l),
				      .q(read_outstanding));
   
   assign 	 ucb_iob_stall_a1 = read_pending & read_outstanding;

   assign 	 write_pending = (indata_buf[`UCB_PKT_HI:`UCB_PKT_LO] == 
				  `UCB_WRITE_REQ) &
        	                 indata_buf_vld;

   // 3'b011 is the encoding for double word.  All writes have to be
   // 64 bits except writes going to PCI.  PCI will instantiate a
   // customized version of UCB.
   assign 	 illegal_write_size = (indata_buf[`UCB_SIZE_HI:`UCB_SIZE_LO] !=
				       3'b011);
   
   
   /************************************************************
    * Inbound interface to local unit
    ************************************************************/
   assign 	 rd_req_vld = read_pending & ~read_outstanding;
   assign 	 wr_req_vld = write_pending & ~illegal_write_size;
   assign 	 thr_id_in = indata_buf[`UCB_THR_HI:`UCB_THR_LO];
   assign 	 buf_id_in = indata_buf[`UCB_BUF_HI:`UCB_BUF_LO];
   assign 	 size_in = indata_buf[`UCB_SIZE_HI:`UCB_SIZE_LO];
   assign 	 addr_in = indata_buf[`UCB_ADDR_HI:`UCB_ADDR_LO];
   assign 	 data_in = indata_buf[`UCB_DATA_HI:`UCB_DATA_LO];
   
   
   /************************************************************
    * Outbound Ack/Nack
    ************************************************************/
   assign 	 ack_buf_wr = rd_ack_vld | rd_nack_vld;
   
   assign 	 ack_buf_vld_next = ack_buf_wr ? 1'b1 :
		                    ack_buf_rd ? 1'b0 :
		                                 ack_buf_vld;
   
   dffrl_ns #(1) ack_buf_vld_ff (.din(ack_buf_vld_next),
				 .clk(clk),
				 .rst_l(rst_l),
				 .q(ack_buf_vld));
   
   dffe_ns #(1) ack_buf_is_nack_ff (.din(rd_nack_vld),
				    .en(ack_buf_wr),
				    .clk(clk),
				    .q(ack_buf_is_nack));

   dffe_ns #(1) ack_buf_is_data128_ff (.din(data128),
				       .en(ack_buf_wr),
				       .clk(clk),
				       .q(ack_buf_is_data128));

   assign 	 ack_typ_out = rd_ack_vld ? `UCB_READ_ACK:
	                                    `UCB_READ_NACK;

   assign 	 ack_buf_in = {data_out,
			       buf_id_out,
			       thr_id_out,
			       ack_typ_out};
   
   dffe_ns #(REG_WIDTH+`UCB_BUF_HI-`UCB_PKT_LO+1) ack_buf_ff (.din(ack_buf_in),
							      .en(ack_buf_wr),
							      .clk(clk),
							      .q(ack_buf));

   assign        ack_buf_vec = ack_buf_is_nack    ? {{REG_WIDTH/UCB_IOB_WIDTH{1'b0}},
                                                     {64/UCB_IOB_WIDTH{1'b1}}} :
                               ack_buf_is_data128 ? {(REG_WIDTH+64)/UCB_IOB_WIDTH{1'b1}} :
                                                    {(64+64)/UCB_IOB_WIDTH{1'b1}};
   

   /************************************************************
    * Outbound Interrupt
    ************************************************************/
   // Assertion: int_buf_wr shoudn't be asserted if int_buf_busy
   assign 	 int_buf_wr = int_vld;
   
   assign 	 int_buf_vld_next = int_buf_wr ? 1'b1 :
		                    int_buf_rd ? 1'b0 :
		                                 int_buf_vld;
   
   dffrl_ns #(1) int_buf_vld_ff (.din(int_buf_vld_next),
				 .clk(clk),
				 .rst_l(rst_l),
				 .q(int_buf_vld));

   assign 	 int_buf_in = {int_vec,
			       int_stat,
			       dev_id,
			       int_thr_id,
			       int_typ};
   
   dffe_ns #(`UCB_INT_VEC_HI-`UCB_PKT_LO+1) int_buf_ff (.din(int_buf_in),
							.en(int_buf_wr),
							.clk(clk),
							.q(int_buf));

   assign 	 int_buf_vec = {{REG_WIDTH/UCB_IOB_WIDTH{1'b0}},
                                {64/UCB_IOB_WIDTH{1'b1}}};

   assign 	 int_busy = int_buf_vld;


   /************************************************************
    * Outbound ack/interrupt Arbitration
    ************************************************************/
   dffrle_ns #(1) int_last_rd_ff (.din(int_buf_rd),
				  .en(ack_buf_rd|int_buf_rd),
				  .rst_l(rst_l),
				  .clk(clk),
				  .q(int_last_rd));
			   
   assign 	 ack_buf_rd = ~outdata_buf_busy & ack_buf_vld &
		              (~int_buf_vld | int_last_rd);
   
   assign 	 int_buf_rd = ~outdata_buf_busy & int_buf_vld &
		              (~ack_buf_vld | ~int_last_rd);

   assign 	 outdata_buf_wr = ack_buf_rd | int_buf_rd;
   
   assign 	 outdata_buf_in = ack_buf_rd ? {ack_buf[REG_WIDTH+`UCB_BUF_HI:`UCB_BUF_HI+1],
						{(`UCB_RSV_HI-`UCB_RSV_LO+1){1'b0}},
						{(`UCB_ADDR_HI-`UCB_ADDR_LO+1){1'b0}},
						{(`UCB_SIZE_HI-`UCB_SIZE_LO+1){1'b0}},
						ack_buf[`UCB_BUF_HI:`UCB_BUF_LO],
						ack_buf[`UCB_THR_HI:`UCB_THR_LO],
						ack_buf[`UCB_PKT_HI:`UCB_PKT_LO]}:
		                               {{REG_WIDTH{1'b0}},
						{(`UCB_INT_RSV_HI-`UCB_INT_RSV_LO+1){1'b0}},
						int_buf[`UCB_INT_VEC_HI:`UCB_INT_VEC_LO],
						int_buf[`UCB_INT_STAT_HI:`UCB_INT_STAT_LO],
						int_buf[`UCB_INT_DEV_HI:`UCB_INT_DEV_LO],
						int_buf[`UCB_THR_HI:`UCB_THR_LO],
						int_buf[`UCB_PKT_HI:`UCB_PKT_LO]};
   
   assign 	 outdata_vec_in = ack_buf_rd ? ack_buf_vec :
	                                       int_buf_vec;
   
   ucb_bus_out #(UCB_IOB_WIDTH, REG_WIDTH) ucb_bus_out (.rst_l(rst_l),
							.clk(clk),
							.outdata_buf_wr(outdata_buf_wr),
							.outdata_buf_in(outdata_buf_in),
							.outdata_vec_in(outdata_vec_in),
							.outdata_buf_busy(outdata_buf_busy),
							.vld(ucb_iob_vld),
							.data(ucb_iob_data),
							.stall(iob_ucb_stall));
   
   
endmodule // ucb_noflow


// Local Variables:
// verilog-library-directories:(".")
// End:







